#include "main.c"
#include "Assembler.c"

double CPU(CMD_Description* Text_Of_The_Program, int Key[]);

double  Array[12] = {};

double CPU(CMD_Description* Text_Of_The_Program, int Key[])
{

    struct Stack* CPU_stack =(struct Stack*)calloc(1,sizeof(struct Stack));
    Stack_Create(CPU_stack,Program_Length);
    int counter = 0;
    int Condition = True;


    while(Condition)
    {

    switch(Text_Of_The_Program[counter].cmd)
    {

    default:
    {
        break;
    }
    case IN:
    {
        Stack_Push(CPU_stack, Text_Of_The_Program[counter].Arguments[0]);
        break;
    }

    case OUT:
    {
        double Result = Stack_Pop(CPU_stack);
        printf("\n%lg", Result);
        break;
    }

    case PUSH:
    {
        Stack_Push(CPU_stack, Text_Of_The_Program[counter].Arguments[0]);
        break;
    }

    case POPR:
    {
        Array[Text_Of_The_Program[counter].Arguments[0]] = Stack_Pop(CPU_stack);
        break;
    }

    case PUSHR:
    {
        Stack_Push(CPU_stack, Array[Text_Of_The_Program[counter].Arguments[0]]);
        break;
    }
    case ADD:
    {
        double Top = Stack_Pop(CPU_stack);
        double Second = Stack_Pop(CPU_stack);
        double Result = Top + Second;
        Stack_Push(CPU_stack, Result);
        break;
    }

    case SUB:
    {
        double Top = Stack_Pop(CPU_stack);
        double Second = Stack_Pop(CPU_stack);
        double Result = Second - Top;
        Stack_Push(CPU_stack, Result);
        break;
    }

    case MUL:
    {
        double Top = Stack_Pop(CPU_stack);
        double Second = Stack_Pop(CPU_stack);
        double Result = Top*Second;
        Stack_Push(CPU_stack, Result);
        break;
    }

    case DIV:
    {
        double Top = Stack_Pop(CPU_stack);
        if (fabs(Top) < DBL_EPSILON)
        {
            printf ("Division by zero");
            Condition = False;
            break;
        }
        double Second = Stack_Pop(CPU_stack);
        double Result = Second / Top;
        Stack_Push(CPU_stack, Result);
        break;
    }

    case SQRT:
    {
        double Top = Stack_Pop(CPU_stack);
        double Result = sqrt(Top);
        Stack_Push(CPU_stack, Result);
        break;
    }

    case JER:
    {
        if (fabs(Array[1] - Array[2]) < DBL_EPSILON)
        {
            counter = Key[Text_Of_The_Program[counter].Arguments[0]];
            continue;
        }
        break;
    }

    case JNER:
    {
        if (fabs(Array[1] - Array[2]) >= DBL_EPSILON)
        {
            counter = Key[Text_Of_The_Program[counter].Arguments[0]];
            continue;
        }
        break;
    }

    case INCR:
    {
        Array[Text_Of_The_Program[counter].Arguments[0]]++;
        break;
    }

    case OUTR:
    {
        printf("%lg\n", Array[Text_Of_The_Program[counter].Arguments[0]]);
        break;
    }

    case MOV:
    {
        Array[Text_Of_The_Program[counter].Arguments[0]] = Array[Text_Of_The_Program[counter].Arguments[1]];
        break;
    }

    case JMP:
    {
        counter = Key[Text_Of_The_Program[counter].Arguments[0]];
        continue;
    }

    case JE:
    {
        double Top = Stack_Pop(CPU_stack);
        double    Second = Stack_Pop(CPU_stack);
        Stack_Push(CPU_stack,    Second);
        Stack_Push(CPU_stack, Top);
        if (fabs(Top-Second) <= DBL_EPSILON)
        {
            counter = Key[Text_Of_The_Program[counter].Arguments[0]];
            continue;
        }
        break;
    }

    case JNE:
    {
        double Top = Stack_Pop(CPU_stack);
        double Second = Stack_Pop(CPU_stack);
        Stack_Push(CPU_stack, Second);
        Stack_Push(CPU_stack, Top);
        if (fabs(Top-Second) > DBL_EPSILON)
        {
            counter = Key[Text_Of_The_Program[counter].Arguments[0]];
            continue;
        }
        break;
    }

    case JA:
    {
        double Top = Stack_Pop(CPU_stack);
        double Second = Stack_Pop(CPU_stack);
        Stack_Push(CPU_stack, Second);
        Stack_Push(CPU_stack, Top);
        if ((Top-Second) > DBL_EPSILON)
        {
            counter = Key[Text_Of_The_Program[counter].Arguments[0]];
            continue;
        }
        break;
    }

    case JB:
    {
        double Top = Stack_Pop(CPU_stack);
        double Second = Stack_Pop(CPU_stack);
        Stack_Push(CPU_stack, Second);
        Stack_Push(CPU_stack, Top);
        if ((Second-Top) > DBL_EPSILON)
        {
            counter = Key[Text_Of_The_Program[counter].Arguments[0]];
            continue;
        }
        break;
    }

    case JNA:
    {
        double Top = Stack_Pop(CPU_stack);
        double Second = Stack_Pop(CPU_stack);
        Stack_Push(CPU_stack, Second);
        Stack_Push(CPU_stack, Top);
        if ((Top-Second) <= DBL_EPSILON)
        {
            counter = Key[Text_Of_The_Program[counter].Arguments[0]];
            continue;
        }
        break;
    }

    case JNB:
    {
        double Top = Stack_Pop(CPU_stack);
        double Second = Stack_Pop(CPU_stack);
        Stack_Push(CPU_stack, Second);
        Stack_Push(CPU_stack, Top);
        if ((Top-Second) >= DBL_EPSILON)
        {
            counter = Key[Text_Of_The_Program[counter].Arguments[0]];
            continue;
        }
        break;
    }

    case CALL:
    {
        Stack_Push(CPU_stack, counter + 1);
        counter = Key[Text_Of_The_Program[counter].Arguments[0]];
        continue;
    }

    case RET:
    {
        counter = (int)Stack_Pop(CPU_stack);
        continue;
    }
    case END:
    {
        Stack_Destruct(CPU_stack);
        Condition = False;
        break;
    }
    }
     counter++;
    }

    CPU_stack = NULL;

    return 0;
}
